/*
 * Copyright (c) 2010 Philip Foulkes <philip.foulkes@gmail.com>
 *
 * This file is free software and is made available under the 
 * terms of the GNU General Public License, version 2, or at your
 * option, any later version
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/hardirq.h>
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <linux/kfifo.h>
#include <linux/list.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/fs.h>

#include "mrp.h"
#include "msrp.h"
#include "mmrp.h"
#include "mvrp.h"
#include "msrp_mrpdu.h"
#include "msrp_private.h"

#include "../include/mrp_attribute_properties.h"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Philip Foulkes <philip.foulkes@gmail.com>");
MODULE_DESCRIPTION("Multiple Stream Reservation Protocol");

// module parameters ===============================
static char * port_name = "eth0";
module_param (port_name, charp, S_IRUGO);
// =================================================

// =================================================
struct read_list 
{
    struct list_head list;
    struct msrp_read_data read_data;
    void * data;
};

void read_list_destroy (struct read_list ** read_list)
{
    kfree ((*read_list)->data);
    kfree (*read_list);
    *read_list = 0;
}
// =================================================

static struct msrp msrp;
static dev_t dev = 0;

static int msrp_mad_join_indication (struct mrp * mrp, int mrp_attribute_type, void * attribute_value, int four_packed_type, int new)
{
    struct msrp_attribute_value_talker_advertise * advertise;
    struct msrp_attribute_value_talker_failed * failed;
    struct msrp_attribute_value_listener * listener;
    
    struct read_list * read_list;
    struct msrp_register_stream * msrp_register_stream;
    struct msrp_register_attach * msrp_register_attach;
    
    int ret;
    
    MSRP_PRINT_INFO ("%s\n", __FUNCTION__);
    
    if (mrp_attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE)
    {
        advertise = (struct msrp_attribute_value_talker_advertise *)attribute_value;
        
        if ((read_list = kmalloc (sizeof(struct read_list), MSRP_GET_CONTEXT_ALLOC_FLAG)))
        {
            if ((read_list->data = kmalloc (sizeof(struct msrp_register_stream), MSRP_GET_CONTEXT_ALLOC_FLAG)))
            {
                msrp_register_stream = read_list->data;
                read_list->read_data.command = COMMAND_NUMBER_REGISTER_STREAM_INDICATION;
                memcpy (msrp_register_stream->stream_id, advertise->stream_id, 8);
                msrp_register_stream->declaration_type = MSRP_DECLARATION_TYPE_ADVERTISE;
                memcpy (&msrp_register_stream->msrp_data_frame_parameters, &advertise->msrp_data_frame_parameters, sizeof(struct msrp_data_frame_parameters));
                memcpy (&msrp_register_stream->msrp_tspec, &advertise->msrp_tspec, sizeof(struct msrp_tspec));
                memcpy (&msrp_register_stream->msrp_priority_and_rank, &advertise->msrp_priority_and_rank, sizeof(struct msrp_priority_and_rank));
                msrp_register_stream->accumulated_latency = advertise->accumulated_latency;
                
                spin_lock (&msrp.msrp_read_data_list_lock);
                list_add_tail (&read_list->list, &msrp.msrp_read_data_list);
                spin_unlock (&msrp.msrp_read_data_list_lock);
                complete (&msrp.completion);
            }
            else kfree (read_list);
        }
        
        if (msrp.msrp_ops && msrp.msrp_ops->msrp_register_stream_indication && (ret = msrp.msrp_ops->msrp_register_stream_indication (advertise->stream_id, MSRP_DECLARATION_TYPE_ADVERTISE,
            &advertise->msrp_data_frame_parameters, &advertise->msrp_tspec, &advertise->msrp_priority_and_rank, advertise->accumulated_latency)))
            return ret;
        return 0;
    }
    else if (mrp_attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_FAILED)
    {
        failed = (struct msrp_attribute_value_talker_failed *)attribute_value;
        
        if ((read_list = kmalloc (sizeof(struct read_list), MSRP_GET_CONTEXT_ALLOC_FLAG)))
        {
            if ((read_list->data = kmalloc (sizeof(struct msrp_register_stream), MSRP_GET_CONTEXT_ALLOC_FLAG)))
            {
                msrp_register_stream = read_list->data;
                read_list->read_data.command = COMMAND_NUMBER_REGISTER_STREAM_INDICATION;
                memcpy (msrp_register_stream->stream_id, failed->stream_id, 8);
                msrp_register_stream->declaration_type = MSRP_DECLARATION_TYPE_FAILED;
                memcpy (&msrp_register_stream->msrp_data_frame_parameters, &failed->msrp_data_frame_parameters, sizeof(struct msrp_data_frame_parameters));
                memcpy (&msrp_register_stream->msrp_tspec, &failed->msrp_tspec, sizeof(struct msrp_tspec));
                memcpy (&msrp_register_stream->msrp_priority_and_rank, &failed->msrp_priority_and_rank, sizeof(struct msrp_priority_and_rank));
                msrp_register_stream->accumulated_latency = failed->accumulated_latency;
                
                spin_lock (&msrp.msrp_read_data_list_lock);
                list_add_tail (&read_list->list, &msrp.msrp_read_data_list);
                spin_unlock (&msrp.msrp_read_data_list_lock);
                complete (&msrp.completion);
            }
            else kfree (read_list);
        }
        
        if (msrp.msrp_ops && msrp.msrp_ops->msrp_register_stream_indication && (ret = msrp.msrp_ops->msrp_register_stream_indication (failed->stream_id, MSRP_DECLARATION_TYPE_FAILED, &failed->msrp_data_frame_parameters, &failed->msrp_tspec, &failed->msrp_priority_and_rank, failed->accumulated_latency)))
            return ret;
        return 0;
    }
    else if (mrp_attribute_type == MSRP_ATTRIBUTE_TYPE_LISTENER)
    {	
        listener = (struct msrp_attribute_value_listener *) attribute_value;
        
        if ((read_list = kmalloc (sizeof(struct read_list), MSRP_GET_CONTEXT_ALLOC_FLAG)))
        {
            if ((read_list->data = kmalloc (sizeof(struct msrp_register_attach), MSRP_GET_CONTEXT_ALLOC_FLAG)))
            {
                msrp_register_attach = read_list->data;
        
                read_list->read_data.command = COMMAND_NUMBER_REGISTER_ATTACH_INDICATION;
                
                memcpy (msrp_register_attach->stream_id, listener->stream_id, 8);
                
                if (four_packed_type == MSRP_FOUR_PACKED_EVENTS_ASKING_FAILED)
                    msrp_register_attach->declaration_type = MSRP_DECLARATION_TYPE_ASKING_FAILED;
                else if (four_packed_type == MSRP_FOUR_PACKED_EVENTS_READY)
                    msrp_register_attach->declaration_type = MSRP_DECLARATION_TYPE_READY;
                else if (four_packed_type == MSRP_FOUR_PACKED_EVENTS_READY_FAILED)
                    msrp_register_attach->declaration_type = MSRP_DECLARATION_TYPE_READY_FAILED;
                else
                {
                    msrp_register_attach->declaration_type = 0;
                    MSRP_PRINT_INFO ("%s\n", "COULD NOT DETERMINE LISTENER TYPE");
                }
                
                spin_lock (&msrp.msrp_read_data_list_lock);
                list_add_tail (&read_list->list, &msrp.msrp_read_data_list);
                spin_unlock (&msrp.msrp_read_data_list_lock);
                complete (&msrp.completion);
            }
        }
        
        if (msrp.msrp_ops && msrp.msrp_ops->msrp_register_attach_indication)
        {
            if (four_packed_type == MSRP_FOUR_PACKED_EVENTS_ASKING_FAILED)
                return msrp.msrp_ops->msrp_register_attach_indication (listener->stream_id, MSRP_DECLARATION_TYPE_ASKING_FAILED);
            else if (four_packed_type == MSRP_FOUR_PACKED_EVENTS_READY)
                return msrp.msrp_ops->msrp_register_attach_indication (listener->stream_id, MSRP_DECLARATION_TYPE_READY);
            else if (four_packed_type == MSRP_FOUR_PACKED_EVENTS_READY_FAILED)
                return msrp.msrp_ops->msrp_register_attach_indication (listener->stream_id, MSRP_DECLARATION_TYPE_READY_FAILED);
            else
            {
                MSRP_PRINT_INFO ("%s\n", "COULD NOT DETERMINE LISTENER TYPE");
            }
        }
        else
            return 0;
    }
    else
    {
        MSRP_PRINT_INFO ("%s %d\n", "COULD NOT DETERMINE ATTRIBUTE TYPE", mrp_attribute_type);
    }
    
    return -EINVAL;
}

static int msrp_mad_leave_indication (struct mrp * mrp, int mrp_attribute_type, void * attribute_value, int four_packed_type)
{
    struct read_list * read_list;
    struct msrp_deregister_stream * msrp_deregister_stream;
    struct msrp_deregister_attach * msrp_deregister_attach;
    
    struct msrp_attribute_value_talker_advertise 	* advertise;
    struct msrp_attribute_value_talker_failed 		* advertise_failed;
    struct msrp_attribute_value_listener			* listen;
    
    MSRP_PRINT_INFO ("%s\n", __FUNCTION__);
    
    if (mrp_attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE)
    {
        advertise = (struct msrp_attribute_value_talker_advertise *)attribute_value;
        
        if ((read_list = kmalloc (sizeof(struct read_list), MSRP_GET_CONTEXT_ALLOC_FLAG)))
        {
            if ((read_list->data = kmalloc (sizeof(struct msrp_deregister_stream), MSRP_GET_CONTEXT_ALLOC_FLAG)))
            {
                msrp_deregister_stream = read_list->data;
                read_list->read_data.command = COMMAND_NUMBER_DEREGISTER_STREAM_INDICATION;
                memcpy (msrp_deregister_stream->stream_id, advertise->stream_id, 8);
                
                spin_lock (&msrp.msrp_read_data_list_lock);
                list_add_tail (&read_list->list, &msrp.msrp_read_data_list);
                spin_unlock (&msrp.msrp_read_data_list_lock);
                complete (&msrp.completion);
            }
            else kfree (read_list);
        }
        
        if (msrp.msrp_ops && msrp.msrp_ops->msrp_deregister_stream_indication)
            return msrp.msrp_ops->msrp_deregister_stream_indication (advertise->stream_id);
        else
            return 0;
    }
    else if (mrp_attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_FAILED)
    {
        advertise_failed = (struct msrp_attribute_value_talker_failed *)attribute_value;
        
        if ((read_list = kmalloc (sizeof(struct read_list), MSRP_GET_CONTEXT_ALLOC_FLAG)))
        {
            if ((read_list->data = kmalloc (sizeof(struct msrp_deregister_stream), MSRP_GET_CONTEXT_ALLOC_FLAG)))
            {
                msrp_deregister_stream = read_list->data;
                read_list->read_data.command = COMMAND_NUMBER_DEREGISTER_STREAM_INDICATION;
                memcpy (msrp_deregister_stream->stream_id, advertise_failed->stream_id, 8);
                
                spin_lock (&msrp.msrp_read_data_list_lock);
                list_add_tail (&read_list->list, &msrp.msrp_read_data_list);
                spin_unlock (&msrp.msrp_read_data_list_lock);
                complete (&msrp.completion);
            }
            else kfree (read_list);
        }
        
        if (msrp.msrp_ops && msrp.msrp_ops->msrp_deregister_stream_indication)
            return msrp.msrp_ops->msrp_deregister_stream_indication (advertise_failed->stream_id);
        else
            return 0;
    }
    else if (mrp_attribute_type == MSRP_ATTRIBUTE_TYPE_LISTENER)
    {
        listen = (struct msrp_attribute_value_listener *) attribute_value;
        
        if ((read_list = kmalloc (sizeof(struct read_list), MSRP_GET_CONTEXT_ALLOC_FLAG)))
        {
            if ((read_list->data = kmalloc (sizeof(struct msrp_deregister_attach), MSRP_GET_CONTEXT_ALLOC_FLAG)))
            {
                msrp_deregister_attach = read_list->data;
                read_list->read_data.command = COMMAND_NUMBER_DEREGISTER_ATTACH_INDICATION;
                memcpy (msrp_deregister_attach->stream_id, listen->stream_id, 8);
                
                spin_lock (&msrp.msrp_read_data_list_lock);
                list_add_tail (&read_list->list, &msrp.msrp_read_data_list);
                spin_unlock (&msrp.msrp_read_data_list_lock);
                complete (&msrp.completion);
            }
            else kfree (read_list);
        }
        
        if (msrp.msrp_ops && msrp.msrp_ops->msrp_deregister_attach_indication)
            return msrp.msrp_ops->msrp_deregister_attach_indication (listen->stream_id);
        else
            return 0;
    }
    
    return -EINVAL;
}

static inline int msrp_compare_talker_advetise 	(u8 * one, u8 * two) { return memcmp (one, two, MSRP_ATTRIBUTE_LENGTH_TALKER_ADVERTISE_ATTRIBUTE_TYPE); 	}
static inline int msrp_compare_talker_failed 	(u8 * one, u8 * two) { return memcmp (one, two, MSRP_ATTRIBUTE_LENGTH_TALKER_FAILED_ATTRIBUTE_TYPE); 		}
static inline int msrp_compare_listener			(u8 * one, u8 * two) { return memcmp (one, two, MSRP_ATTRIBUTE_LENGTH_LISTENER_ATTRIBUTE_TYPE); 			}

static mrp_attribute_value_compare msrp_get_attribute_value_compare (int attribute_type)
{
    if (attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE)
        return (mrp_attribute_value_compare) msrp_compare_talker_advetise;
    else if (attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_FAILED)
        return (mrp_attribute_value_compare) msrp_compare_talker_failed;
    else if (attribute_type == MSRP_ATTRIBUTE_TYPE_LISTENER)
        return (mrp_attribute_value_compare) msrp_compare_listener;
    else
        return NULL;
}

static int msrp_get_attribute_length (int attribute_type)
{
    if (attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE)
        return MSRP_ATTRIBUTE_LENGTH_TALKER_ADVERTISE_ATTRIBUTE_TYPE;
    else if (attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_FAILED)
        return MSRP_ATTRIBUTE_LENGTH_TALKER_FAILED_ATTRIBUTE_TYPE;
    else if (attribute_type == MSRP_ATTRIBUTE_TYPE_LISTENER)
        return MSRP_ATTRIBUTE_LENGTH_LISTENER_ATTRIBUTE_TYPE;
    else
        return 0;
}

static int msrp_increment_first_value (int attribute_type, void * first_value, int how_much)
{
    #define inc_stream_id(stream_id) { int i; int j; \
        for (j = 0; j < how_much; ++j) \
        { \
            for (i = 7; i >= 0; --i) \
            { \
                stream_id [i]++; \
                if (stream_id [i] != 0) \
                    break; \
        } } }
    
    #define inc_talker(talker_attribute) { int i; int j; \
        inc_stream_id (((talker_attribute)->stream_id)) \
        for (j = 0; j < how_much; ++j) \
        { \
            for (i = 5; i >= 0; --i) \
            { \
                ((talker_attribute)->msrp_data_frame_parameters.destination_address[i])++; \
                if ((talker_attribute)->msrp_data_frame_parameters.destination_address [i] != 0) \
                    break; \
        } } }
    
    if (attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE)
        inc_talker((struct msrp_attribute_value_talker_advertise *)first_value)
    else if (attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_FAILED)
        inc_talker((struct msrp_attribute_value_talker_failed *)first_value)
    else if (attribute_type == MSRP_ATTRIBUTE_TYPE_LISTENER)
        inc_stream_id(((struct msrp_attribute_value_listener *) first_value)->stream_id);
    
    return 0;
}

static int msrp_does_need_four_packed_events (int attribute_type)
{
    if (attribute_type == MSRP_ATTRIBUTE_TYPE_LISTENER)	return 1;
    else return 0;
}

static struct mrp_ops msrp_mrp_ops = {
    .mrp_mad_join_indication 			= msrp_mad_join_indication,
    .mrp_mad_leave_indication 			= msrp_mad_leave_indication,
    .mrp_get_attribute_value_compare 	= msrp_get_attribute_value_compare,
    .mrp_get_attribute_length 			= msrp_get_attribute_length,
    .mrp_increment_first_value			= msrp_increment_first_value,
    .mrp_does_need_four_packed_events 	= msrp_does_need_four_packed_events,
};

void msrp_register (struct msrp_ops * msrp_ops_)
{		
    /*if (	! msrp_ops_
        ||  ! msrp_ops_->msrp_register_attach_indication
        ||	! msrp_ops_->msrp_deregister_attach_indication
        ||	! msrp_ops_->msrp_register_stream_indication
        ||	! msrp_ops_->msrp_deregister_stream_indication )
        return NULL;*/
        
    msrp.msrp_ops = msrp_ops_;
}
EXPORT_SYMBOL (msrp_register);

int msrp_register_attach_request (u8 stream_id [8], int delcaration_type)
{
    struct msrp_attribute_value_listener msrp_attribute_value_listener;
    struct mrp_attribute_properties * mrp_attribute_properties;
    struct msrp_attribute_value_talker_advertise * advertise;
    struct msrp_attribute_value_talker_failed * advertise_failed;
    struct mrp_iterator * iterator;
    int found_talker;
    int four_packed_type;
    struct mrp * mrp;
    int ret;
    
    MSRP_PRINT_INFO ("%s\n", __FUNCTION__);
    
    mrp = (struct mrp *)&msrp;
    found_talker = 0;
    
    if (	delcaration_type != MSRP_DECLARATION_TYPE_ASKING_FAILED
        &&	delcaration_type != MSRP_DECLARATION_TYPE_READY
        &&  delcaration_type != MSRP_DECLARATION_TYPE_READY_FAILED )
        return -EINVAL;
    
    memcpy (msrp_attribute_value_listener.stream_id, stream_id, 8);
    
    switch (delcaration_type)
    {
        case MSRP_DECLARATION_TYPE_ASKING_FAILED: 	{ four_packed_type = MSRP_FOUR_PACKED_EVENTS_ASKING_FAILED; break; }
        case MSRP_DECLARATION_TYPE_READY:			{ four_packed_type = MSRP_FOUR_PACKED_EVENTS_READY; 		break; }
        case MSRP_DECLARATION_TYPE_READY_FAILED:	{ four_packed_type = MSRP_FOUR_PACKED_EVENTS_READY_FAILED; 	break; }
        default: 									{ four_packed_type = MSRP_FOUR_PACKED_EVENTS_IGNORE; }
    }
    
    iterator = mrp_get_mrp_attribute_iterator (mrp);
    
    while (!mrp_iterator_at_end(iterator))
    {
        mrp_attribute_properties = (struct mrp_attribute_properties *)mrp_iterator_get(iterator);

        if (mrp_attribute_properties_get_attribute_type(mrp_attribute_properties) == MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE)
        {
            advertise = mrp_attribute_properties_get_attribute_value(mrp_attribute_properties);
            if (memcmp(advertise->stream_id, stream_id, 8) == 0)
            {
                found_talker = 1;
                if ((ret = mvrp_es_register_vlan_member(advertise->msrp_data_frame_parameters.vlan_identifier)))
                    return ret;
            }
        }
        else if (mrp_attribute_properties_get_attribute_type(mrp_attribute_properties) == MSRP_ATTRIBUTE_TYPE_TALKER_FAILED)
        {
            advertise_failed = mrp_attribute_properties_get_attribute_value(mrp_attribute_properties);
            if (memcmp(advertise_failed->stream_id, stream_id, 8) == 0)
            {
                found_talker = 1;
                if (delcaration_type == MSRP_DECLARATION_TYPE_READY || delcaration_type == MSRP_DECLARATION_TYPE_READY_FAILED)
                    four_packed_type = MSRP_FOUR_PACKED_EVENTS_ASKING_FAILED;
            }
        }
        
        mrp_attribute_properties_destroy(&mrp_attribute_properties);
        
        mrp_iterator_next(iterator);
    }
    
    if (!found_talker) /* talker has not advertised the requested stream */
        four_packed_type = MSRP_FOUR_PACKED_EVENTS_ASKING_FAILED;
    
    if ((ret = mrp_mad_join_request_notify (mrp, MSRP_ATTRIBUTE_TYPE_LISTENER, &msrp_attribute_value_listener, four_packed_type, 0)))
        return ret;
    
    return ret;
}
EXPORT_SYMBOL (msrp_register_attach_request);

int msrp_deregister_attach_request (u8 stream_id [8])
{
    struct msrp_attribute_value_listener msrp_attribute_value_listener;
    int ret;
    
    MSRP_PRINT_INFO ("%s\n", __FUNCTION__);
    
    memcpy (msrp_attribute_value_listener.stream_id, stream_id, 8);
    
    if ((ret = mrp_mad_leave_request_notify ((struct mrp *) &msrp, MSRP_ATTRIBUTE_TYPE_LISTENER, &msrp_attribute_value_listener)))
        return ret;
        
    return ret;
}
EXPORT_SYMBOL (msrp_deregister_attach_request);

int msrp_register_stream_request (u8 stream_id [8], int declaration_type, struct msrp_data_frame_parameters * msrp_data_frame_parameters, struct msrp_tspec * msrp_tspec, struct msrp_priority_and_rank * msrp_priority_and_rank, u32 accumulated_latency)
{
#define fill_talker_struct(talker_struct) 	\
    memcpy (talker_struct.stream_id, stream_id, 8);	\
    memcpy (&talker_struct.msrp_data_frame_parameters, msrp_data_frame_parameters, sizeof(struct msrp_data_frame_parameters)); \
    memcpy (&talker_struct.msrp_tspec, msrp_tspec, sizeof (struct msrp_tspec)); \
    memcpy (&talker_struct.msrp_priority_and_rank, msrp_priority_and_rank, sizeof (struct msrp_priority_and_rank)); \
    talker_struct.accumulated_latency = accumulated_latency;
        
    struct msrp_attribute_value_talker_failed msrp_attribute_value_talker_failed;
    
    MSRP_PRINT_INFO ("%s\n", __FUNCTION__);
    
    if (declaration_type == MSRP_DECLARATION_TYPE_ADVERTISE)
    {
        fill_talker_struct (msrp_attribute_value_talker_failed);
        return mrp_mad_join_request_notify ((struct mrp *) &msrp, MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE, (struct msrp_attribute_value_talker_advertise *) &msrp_attribute_value_talker_failed, MSRP_FOUR_PACKED_EVENTS_IGNORE, 0);
    }
    else if (declaration_type == MSRP_DECLARATION_TYPE_FAILED)
    {
        fill_talker_struct (msrp_attribute_value_talker_failed);
        return mrp_mad_join_request_notify ((struct mrp *) &msrp, MSRP_ATTRIBUTE_TYPE_TALKER_FAILED, &msrp_attribute_value_talker_failed, MSRP_FOUR_PACKED_EVENTS_IGNORE, 0);
    }
    
    return -EINVAL;
}
EXPORT_SYMBOL (msrp_register_stream_request);

static int msrp_deregister_stream_request_message_thread (void * data)
{
    int ret;
    int attribute_type;
    void * attribute_value;
    struct mrp_iterator * mrp_iterator;
    struct mrp_attribute_properties * attribute_props;
    
    MSRP_PRINT_INFO ("%s\n", __FUNCTION__);
    
    if ((mrp_iterator = mrp_get_mrp_attribute_iterator ((struct mrp *)&msrp)))
    {
        while (!mrp_iterator_at_end (mrp_iterator))
        {
            if ((attribute_props = (struct mrp_attribute_properties *) mrp_iterator_get (mrp_iterator)))
            {
                attribute_type 	= mrp_attribute_properties_get_attribute_type (attribute_props);
                attribute_value = mrp_attribute_properties_get_attribute_value(attribute_props);
                
                if ((attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE || attribute_type == MSRP_ATTRIBUTE_TYPE_TALKER_FAILED)
                        && memcmp (attribute_value, (u8 *)data, 8) == 0)
                {
                    ret = mrp_mad_leave_request_notify ((struct mrp *)&msrp, attribute_type, attribute_value);	
                    mrp_attribute_properties_destroy (&attribute_props);
                    return ret;
                }
                
                mrp_attribute_properties_destroy (&attribute_props);
                mrp_iterator_next (mrp_iterator);
            }
        }
    }
    
    return -EINVAL;
}

int msrp_deregister_stream_request (u8 stream_id [8])
{	
    MSRP_PRINT_INFO ("%s\n", __FUNCTION__);
    
    return mrp_call_function_on_message_thread ((struct mrp *)&msrp, msrp_deregister_stream_request_message_thread, stream_id);
}
EXPORT_SYMBOL (msrp_deregister_stream_request);


// =========================================================================================================
static int msrp_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg)
{
    struct msrp_register_stream 		msrp_register_stream; 
    struct msrp_deregister_stream		msrp_deregister_stream;
    struct msrp_register_attach 		msrp_register_attach;
    struct msrp_deregister_attach		msrp_deregister_attach;
    
    if (_IOC_TYPE (cmd) != MSRP_IOC_MAGIC || _IOC_NR (cmd) > MSRP_IOC_MAXNR)
        return -ENOTTY;
        
    if (	((_IOC_DIR (cmd) & _IOC_READ)  && ! access_ok (VERIFY_WRITE, (void __user *)arg, _IOC_SIZE (cmd)))
        ||	((_IOC_DIR (cmd) & _IOC_WRITE) && ! access_ok (VERIFY_READ,  (void __user *)arg, _IOC_SIZE (cmd))) )
        return -EFAULT;
        
    switch(cmd)
    {
        case MSRP_IOC_S_REGISTER_STREAM:
            if (copy_from_user (&msrp_register_stream, (struct msrp_register_stream __user *)arg, sizeof(struct msrp_register_stream)))
                return -EFAULT;				
            return msrp_register_stream_request (msrp_register_stream.stream_id, msrp_register_stream.declaration_type,
                    &msrp_register_stream.msrp_data_frame_parameters, &msrp_register_stream.msrp_tspec,
                    &msrp_register_stream.msrp_priority_and_rank, msrp_register_stream.accumulated_latency);
            
        case MSRP_IOC_S_DEREGISTER_STREAM:
            if (copy_from_user (&msrp_deregister_stream, (struct msrp_deregister_stream __user *)arg, sizeof(struct msrp_deregister_stream)))
                return -EFAULT;
            return msrp_deregister_stream_request (msrp_deregister_stream.stream_id);
            
        case MSRP_IOC_S_REGISTER_ATTACH:
            if (copy_from_user (&msrp_register_attach, (struct msrp_register_attach __user *)arg, sizeof(struct msrp_register_attach)))
                return -EFAULT;
            return msrp_register_attach_request (msrp_register_attach.stream_id, msrp_register_attach.declaration_type);
            
        case MSRP_IOC_S_DEREGISTER_ATTACH:
            if (copy_from_user (&msrp_deregister_attach, (struct msrp_deregister_attach __user *)arg, sizeof(struct msrp_deregister_attach)))
                return -EFAULT;
            return msrp_deregister_attach_request (msrp_deregister_attach.stream_id);
            
        default:
            return -ENOTTY;
    }
    
    return 0;
}

static int msrp_read (struct file * file, char __user * data, size_t count, loff_t * off)
{
    struct read_list * read_list;
    int empty;
    int size = 0;
    int ret;
    
    //MSRP_PRINT_INFO("%s 01\n", __FUNCTION__);
    
    spin_lock (&msrp.msrp_read_data_list_lock);
    empty = list_empty(&msrp.msrp_read_data_list);
    spin_unlock (&msrp.msrp_read_data_list_lock);
    
    //MSRP_PRINT_INFO("%s 02\n", __FUNCTION__);
    
    if (!empty || !(ret = wait_for_completion_interruptible_timeout(&msrp.completion, HZ)) )
    {	
        //MSRP_PRINT_INFO("%s 03\n", __FUNCTION__);
        
        spin_lock (&msrp.msrp_read_data_list_lock);
        empty = list_empty(&msrp.msrp_read_data_list);
        spin_unlock (&msrp.msrp_read_data_list_lock);
        
        //MSRP_PRINT_INFO("%s 04\n", __FUNCTION__);
        
        if (!empty)
        {
            //MSRP_PRINT_INFO("%s 05\n", __FUNCTION__);
            
            spin_lock (&msrp.msrp_read_data_list_lock);
            read_list = list_entry (msrp.msrp_read_data_list.next, struct read_list, list);
            list_del (msrp.msrp_read_data_list.next);
            spin_unlock (&msrp.msrp_read_data_list_lock);
            
            //MSRP_PRINT_INFO("%s 06\n", __FUNCTION__);
            
            if (read_list->read_data.command == COMMAND_NUMBER_REGISTER_ATTACH_INDICATION)
            {
                MSRP_PRINT_INFO ("COMMAND COMMAND_NUMBER_REGISTER_ATTACH_INDICATION\n");
                size = sizeof (struct msrp_register_attach);
            }
            else if (read_list->read_data.command == COMMAND_NUMBER_DEREGISTER_ATTACH_INDICATION)
            {
                MSRP_PRINT_INFO ("COMMAND COMMAND_NUMBER_DEREGISTER_ATTACH_INDICATION\n");
                size = sizeof (struct msrp_deregister_attach);
            }
            else if (read_list->read_data.command == COMMAND_NUMBER_REGISTER_STREAM_INDICATION)
            {
                MSRP_PRINT_INFO ("COMMAND COMMAND_NUMBER_REGISTER_STREAM_INDICATION\n");
                size = sizeof (struct msrp_register_stream);
            }
            else if (read_list->read_data.command == COMMAND_NUMBER_DEREGISTER_STREAM_INDICATION)
            {
                MSRP_PRINT_INFO ("COMMAND COMMAND_NUMBER_DEREGISTER_STREAM_INDICATION\n");
                size = sizeof (struct msrp_deregister_stream);
            }
            else
            {
                MSRP_PRINT_INFO ("COMMAND NOT RECOGNISED %i\n", read_list->read_data.command);
                return 0;
            }
            
            if (count >= size + sizeof (struct msrp_read_data))
            {
                if (	! copy_to_user (data, &read_list->read_data, sizeof (struct msrp_read_data))
                    &&	! copy_to_user (data + sizeof (struct msrp_read_data), read_list->data, size))
                {
                    read_list_destroy (&read_list);
                    return size + sizeof (struct msrp_read_data);
                }
                else
                {
                    read_list_destroy (&read_list);
                    return -1;
                }
            }
            else
            {
                MSRP_PRINT_INFO ("COUNT TOO SMALL\n");
            }
        }
    }
    
    //MSRP_PRINT_INFO("%s 08 ret %i\n", __FUNCTION__, ret);	
    return 0;
}

int msrp_release (struct inode * inode, struct file * filp)
{
    MSRP_PRINT_INFO("%s\n", __FUNCTION__);
    return mrp_leave_request_on_all_attributes_notify (&msrp.mrp);
}

int msrp_open (struct inode * inode, struct file * filp)
{
    MSRP_PRINT_INFO("%s\n", __FUNCTION__);
    return 0;
}

static struct file_operations msrp_fops = {
    .owner =    THIS_MODULE,
    .ioctl =    msrp_ioctl,
    .read  = 	msrp_read,
    .open  =    msrp_open,
    .release = 	msrp_release,
};
// =========================================================================================================


static void msrp_exit (void);

static int msrp_init (void)
{
    int ret;
    
    MSRP_PRINT_INFO ("%s\n", "MSRP Initialising");
    
    memset (&msrp, 0, sizeof (struct msrp));
    
    init_completion (&msrp.completion);
    spin_lock_init (&msrp.msrp_read_data_list_lock);
    INIT_LIST_HEAD (&msrp.msrp_read_data_list);
    
    if ( 	(ret = alloc_chrdev_region (&dev, 0, 1, "msrp"))
        ||	(ret = mrp_init ((struct mrp *)&msrp, mrp_application_address_msrp, MRP_ETHERTYPE_MSRP, port_name, &msrp_mrp_ops, MSRP_PROTOCOL_VERSION, 1)) 
        ||	(ret = mrp_periodic_disable_notify ((struct mrp *)&msrp))
        )
        goto fail;
        
    msrp.dev_t_used = 1;

    cdev_init(&msrp.cdev, &msrp_fops);
    msrp.cdev.owner = THIS_MODULE;
    msrp.cdev.ops = &msrp_fops;
    if ( (ret = cdev_add (&msrp.cdev, dev, 1)) )
        goto fail;
    msrp.cdev_init = 1;
    
    MSRP_PRINT_INFO ("%s\n", "MSRP Initialisation Complete");
    return 0;

fail:
    MSRP_PRINT_INFO ("%s\n", "MSRP Initialisation Failed");
    msrp_exit();
    return ret;
}

static void msrp_exit (void)
{
    MSRP_PRINT_INFO ("%s\n", "MSRP Cleaning Up");
    
    if (msrp.cdev_init)
        cdev_del(&msrp.cdev);
    if (msrp.dev_t_used)
        unregister_chrdev_region(dev, 1);
        
    mrp_clean_up ((struct mrp *)&msrp);
  
    MSRP_PRINT_INFO ("%s\n", "MSRP Clean Up Complete");
}

module_init (msrp_init);
module_exit (msrp_exit);
